import { trim } from 'lodash-es'
export type ContextData = Record<string, string | number | null>
export interface BackendCondition {
  /**
   * # 参数名
   */
  field: string
  /**
   * 参数值
   */
  value: string | number | null | ''
  /**
   * # 查询方式
   * ---
   * - eq: 等于
   * - ne: 不等于
   * - lt: 小于
   * - gt: 大于
   * - le: 小于等于
   * - ge: 大于等于
   * - like: 模糊查询
   * - in: in查询
   */
  operate: 'eq' | 'ne' | 'lt' | 'gt' | 'le' | 'ge' | 'like' | 'in'
}

/**
 * # 前端请求条件
 */
export interface FrontCondition extends BackendCondition {
  /**
   * # 值类型
   * ---
   * 1. 用户设置值
   * 2. 系统变量（变量名格式约束：1.全大写 2.以 SYS_ 开头）
   * 3. 上下文变量（变量名格式约束：1.全大写 2.以 CTX_ 开头）
   * 4. 特殊值
   */
  valType: 1 | 2 | 3 | 4

  /**
   * # 参数值
   * ---
   * - 当值类型为1时：设置的值，就是实际请求值
   * - 当值类型为2时：设置的值，为系统变量名
   * - 当值类型为3时：设置的值，为上下文变量名
   * - 当值类型为4时：NULL表示值设置为null, EMPTY表示值设置为空字符串
   */
  value: string | number | null | ''

  /**
   * # 参数备注信息
   */
  remark?: string
}

/**
 * # 后台查询条件建造者
 */
export class BackendConditionBuilder {
  /**
   * # 如果val是字符串类型，则去除首尾空格
   * @param val 待处理值
   * @returns
   */
  private ifStrDoTrim<T>(val: T) {
    if (typeof val === 'string') {
      return trim(val)
    } else {
      return val
    }
  }
  /**
   * # 判断tmp是否为null或undefined或空字符串
   * @param tmp 待识别值
   * @returns 返回true，表示tmp是null或undefined或空字符串
   */
  private isNullUndefinedOrEmpty<T>(tmp: T) {
    if (tmp === null || tmp === undefined) {
      return true
    } else if (typeof tmp === 'string' && trim(tmp) === '') {
      return true
    } else {
      return false
    }
  }
  /**
   * # 构建用户输入的查询条件（valType为1的）
   * ---
   * - 如果最终返回的是 `undefined`, 则认为是无效条件，最终的查询条件中就不会包含该条件
   * - 如果字段值是字符串，则会自动去除首尾空格
   * @param frontCondition 前端条件
   * @param contextData 上下文数据
   * @returns 如果frontCondition.value为null或undefined， 则返回undefined，否则返回 BackendCondition 对象
   */
  private buildNormCondition(
    frontCondition: FrontCondition,
    contextData: ContextData
  ): BackendCondition | undefined {
    const { field, operate, value } = frontCondition
    if (value === undefined || value === null || this.isNullUndefinedOrEmpty(field)) {
      return undefined
    }
    const realField = this.ifStrDoTrim(field)
    const realValue = this.ifStrDoTrim(value)
    const backendCondition: BackendCondition = {
      field: realField,
      value: realValue,
      operate
    }
    return backendCondition
  }
  /**
   * # 构建系统变量查询条件（valType为2的）
   * ---
   * - 系统变量从 `window._SYS_VAR` 中获取
   * - 如果最终返回的是 `undefined`, 则认为是无效条件，最终的查询条件中就不会包含该条件
   * - 如果最终的字段值是字符串，则会自动去除首尾空格
   * @param frontCondition 前端条件
   * @param contextData 上下文数据
   * @returns 如果frontCondition.value为null或undefined，或 获取到的环境变量值为undefined, 则返回undefined，否则返回 BackendCondition 对象
   */
  private buildSystemVarCondition(
    frontCondition: FrontCondition,
    contextData: ContextData
  ): BackendCondition | undefined {
    const { field, operate, value } = frontCondition
    if (value === null || value === undefined || this.isNullUndefinedOrEmpty(field)) {
      return undefined
    }
    // @ts-ignore
    let realVal = window._SYS_VAR && window._SYS_VAR[this.ifStrDoTrim(value)]
    if (realVal !== undefined) {
      const realField = this.ifStrDoTrim(field)
      realVal = this.ifStrDoTrim(realVal)
      return { field: realField, value: realVal, operate }
    }
    return undefined
  }
  /**
   * # 构建上下文变量查询条件（valType为3的）
   * ---
   * - 如果最终返回的是 `undefined`, 则认为是无效条件，最终的查询条件中就不会包含该条件
   * - 如果最终的字段值是字符串，则会自动去除首尾空格
   * @param frontCondition 前端条件
   * @param contextData 上下文数据
   * @returns 如果frontCondition.value为null或undefined，或 获取到的上下文变量值为undefined, 则返回undefined，否则返回 BackendCondition 对象
   */
  private buildContextVarCondition(
    frontCondition: FrontCondition,
    contextData: ContextData
  ): BackendCondition | undefined {
    const { field, operate, value } = frontCondition
    if (value === null || value === undefined || this.isNullUndefinedOrEmpty(field)) {
      return undefined
    }
    let realVal = contextData[this.ifStrDoTrim(value) as string]
    if (realVal !== undefined) {
      const realField = this.ifStrDoTrim(field)
      realVal = this.ifStrDoTrim(realVal)
      // @ts-ignore
      return { field: realField, value: realVal, operate }
    } else {
      return undefined
    }
  }

  /**
   * # 构建特殊值查询条件（valType为4的）
   * ---
   * - 如果最终返回的是 `undefined`, 则认为是无效条件，最终的查询条件中就不会包含该条件
   * - 如果最终的字段值是字符串，则会自动去除首尾空格
   * @param frontCondition 前端条件
   * @param contextData 上下文数据
   * @returns 如果frontCondition.value为null或undefined，或 最后的特殊值为undefined, 则返回undefined，否则返回 BackendCondition 对象
   */
  private buildSpecailCondition(
    frontCondition: FrontCondition,
    contextData: ContextData
  ): BackendCondition | undefined {
    const { field, value, operate } = frontCondition
    if (value === null || value === undefined || this.isNullUndefinedOrEmpty(field)) {
      return undefined
    }
    let realValue: null | string | undefined = undefined
    switch (this.ifStrDoTrim(value)) {
      case 'NULL': {
        realValue = null
        break
      }
      case 'EMPTY': {
        realValue = ''
        break
      }
    }
    if (realValue === undefined) {
      return undefined
    }
    const realField = this.ifStrDoTrim(field)
    realValue = this.ifStrDoTrim(realValue)
    return { field: realField, value: realValue, operate }
  }

  /**
   * # 后台查询条件建造方法
   * ---
   * - 方法内部会自动清除最终结果中的 `field` 和 `value` 字段中的前后空格
   * - 对于无效的查询条件会被忽略(即：该条件不会出现在最终结果中)
   * ---
   * 符合以下任意一条规则的都是无效查询条件:
   * - field为null或undeinfed或空字符串
   * - valType为1 且 value是 null 或 undefined
   * - valType为2 且 最终取到的环境变量值为 undefined
   * - valType为3 且 最终的特殊值为 undefined
   *
   * @param conditionArr 自定义查询条件
   * @param contextData 上下文
   */
  build(conditionArr: FrontCondition[], contextData: ContextData = {}): BackendCondition[] {
    const backendConditionArr: BackendCondition[] = []
    let backendCondition: BackendCondition | undefined
    conditionArr.map((item) => {
      switch (item.valType) {
        case 1: {
          backendCondition = this.buildNormCondition(item, contextData)
          break
        }
        case 2: {
          backendCondition = this.buildSystemVarCondition(item, contextData)
          break
        }
        case 3: {
          backendCondition = this.buildContextVarCondition(item, contextData)
          break
        }
        case 4: {
          backendCondition = this.buildSpecailCondition(item, contextData)
          break
        }
      }
      if (backendCondition) {
        backendConditionArr.push(backendCondition)
      }
    })
    return backendConditionArr
  }
}
